﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using SQLite4Unity3d;

#nullable enable

namespace MiHoMiao.DataBase
{
    public class SqlAccess : IDisposable
    {
        public SQLiteConnection Connection { get; }
        
        public SqlAccess(string dataBasePath) =>
            Connection = new SQLiteConnection(dataBasePath,
                SQLiteOpenFlags.ReadWrite | SQLiteOpenFlags.Create
            );

        /// <summary>
        /// 在数据库上执行 "create table if not exist" 操作.
        /// 表名是根据泛型 T 的类型名;
        /// 可以稍后通过调用 GetMapping 访问此表。
        /// </summary>
        public int CreateTable<T>() => Connection.CreateTable<T>();

        /// <summary>
        /// 在数据库上执行 "drop table".
        /// 注意: 这是不可逆的！
        /// </summary>
        public int DropTable<T>() => Connection.DropTable<T>();
        
        /// <summary>
        /// 插入指定对象, 如果具有主键, 则自增主键.
        /// </summary>
        public int Insert<T>(T item) => Connection.Insert(item, typeof(T));
        
        /// <summary>
        /// 将指定的可枚举对象插入表中.
        /// </summary>
        public int InsertAll<T>(IEnumerable<T> items) => Connection.InsertAll(items, typeof(T));
        
        /// <summary>
        /// 根据对象的主键, 更新表中该对象其他的所有数据;
        /// 当对应的主键不存在时, 将不会发生任何事!
        /// 注意: 该对象需要具有主键!
        /// </summary>
        public int Update<T>(T item) => Connection.Update(item, typeof(T));

        /// <summary>
        /// 根据主键删除一个元素.
        /// 注意: 该对象需要具有主键!
        /// </summary>
        public int Delete<T>(object primaryKey) => Connection.Delete<T>(primaryKey);
        
        /// <summary>
        /// 根据对象删除一个元素.
        /// 注意: 该对象需要具有主键!
        /// </summary>
        public int Delete<T>(T objectToDelete) => Connection.Delete(objectToDelete);
        
        /// <summary>
        /// 尝试从表中检索具有给定主键的对象.
        /// 找不到时返回 default(T).
        /// 注意: 该对象需要具有主键!
        /// </summary>
        public T? Query<T>(object primaryKey) where T : new() => Connection.Find<T>(primaryKey);
        
        /// <summary>
        /// 尝试从表中检索具有给定主键的对象.
        /// 找不到时返回 default(T).
        /// 注意: 该对象需要具有主键!
        /// </summary>
        public T? QueryAt<T>(int primaryKey) where T : new() => Connection.Find<T>(primaryKey);
        
        /// <summary>
        /// 尝试从表中检索满足指定表达式的对象.
        /// 找不到时返回 default(T).
        /// </summary>
        public T? Query<T>(Expression<Func<T, bool>> predicate) where T : new() => Connection.Find(predicate);

        /// <summary>
        /// 尝试从表中检索满足指定表达式的对象.
        /// 找不到时返回 default(T).
        /// </summary>
        public List<T> Query<T>(string sql) where T : new() => Connection.Query<T>(sql);
        
        /// <summary>
        /// 尝试从表中检索具有给定主键的对象.
        /// 找不到时将会抛出异常.
        /// 注意: 该对象需要具有主键!
        /// </summary>
        public T Load<T>(object primaryKey) where T : new() => Connection.Get<T>(primaryKey);

        /// <summary>
        /// 返回指定类型的表对象.
        /// </summary>
        public TableMapping GetMapping<T>() => Connection.GetMapping(typeof(T));
        
        /// <summary>
        /// 执行一条 sql 语句.
        /// 返回因该语句导致的变化行数.
        /// </summary>
        public int Execute(string sql) => Connection.Execute(sql);
        
        public void Dispose()
        {
            Connection.Close();
            Connection.Dispose();
        }
    }
}